package restful

import (
	"moss/api"
	"moss/conf"
	"moss/proto"
	"moss/status"
	"net/http"
	"strconv"
)

func PutObjectHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "PutObjectHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			contentChecksum := req.Header.Get(conf.HTTPHeaderContentMD5)
			encryption := req.Header.Get(conf.HTTPHeaderOssServerSideEncryption)
			permission := getHeaderString(req, conf.HTTPHeaderOssACL, conf.PermissionObjectDefault)

			objectTag, rc := api.PutObject(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req),
				req.Body, req.ContentLength, contentChecksum, encryption, permission, getUserMeta(req))

			if rc == status.Success {
				sendSuccess(requestId, w, map[string]string{conf.HTTPHeaderEtag: objectTag})
			}

			return rc
		})
}

func CopyObjectHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "CopyObjectHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			copySourceIfMatch := req.Header.Get(conf.HTTPHeaderOssCopySourceIfMatch)
			copySourceIfNoneMatch := req.Header.Get(conf.HTTPHeaderOssCopySourceIfNoneMatch)
			copySourceIfUnmodified := req.Header.Get(conf.HTTPHeaderOssCopySourceIfUnmodifiedSince)
			copySourceIfModified := req.Header.Get(conf.HTTPHeaderOssCopySourceIfModifiedSince)
			encryption := req.Header.Get(conf.HTTPHeaderOssServerSideEncryption)
			objectACL := getHeaderString(req, conf.HTTPHeaderOssObjectACL, conf.PermissionObjectDefault)
			metaDirective := getHeaderString(req, conf.HTTPHeaderOssMetadataDirective, conf.OssMetadataDirectiveCopy)

			copyObjectResult, rc := api.CopyObject(requestId, getAccessKeyId(req),
				getCopySourceBucketName(req), getCopySourceObjectName(req),
				getBucketName(req), getObjectName(req),
				copySourceIfModified, copySourceIfUnmodified,
				copySourceIfMatch, copySourceIfNoneMatch,
				metaDirective, encryption, objectACL, getUserMeta(req))

			if rc != status.Success {
				return rc
			}

			sendXmlDocument(copyObjectResult, http.StatusOK, requestId, w, nil)
			return status.Success
		})
}

func GetObjectHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "GetObjectHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			sendRange := req.Header.Get(conf.HTTPHeaderRange)
			ifModifiedSince := req.Header.Get(conf.HTTPHeaderIfModifiedSince)
			ifUnmodifiedSince := req.Header.Get(conf.HTTPHeaderIfUnmodifiedSince)
			ifMatch := req.Header.Get(conf.HTTPHeaderIfMatch)
			ifNoneMatch := req.Header.Get(conf.HTTPHeaderIfNoneMatch)

			objectInfo, rc := api.GetObject(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req),
				ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch)

			if rc != status.Success {
				return rc
			}

			sendObjectContent(w, req, objectInfo, requestId, http.StatusOK, sendRange)
			return status.Success
		})
}

func AppendObjectHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "AppendObjectHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			positionStr := req.URL.Query().Get("position")
			contentDispositon := req.Header.Get(conf.HTTPHeaderContentDisposition)
			contentChecksum := req.Header.Get(conf.HTTPHeaderContentMD5)
			encryption := req.Header.Get(conf.HTTPHeaderOssServerSideEncryption)
			permission := getHeaderString(req, conf.HTTPHeaderOssACL, conf.PermissionObjectDefault)

			objectMeta, rc := api.AppendObject(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req), contentDispositon,
				contentChecksum, encryption, permission, positionStr, getUserMeta(req), req.ContentLength, req.Body)
			if rc != status.Success {
				return rc
			}

			headers := map[string]string{conf.HTTPHeaderOssNextAppendPosition: strconv.FormatInt(objectMeta.Size, 10),
				conf.HTTPHeaderOssCRC64: objectMeta.Crc64Checksum, conf.HTTPHeaderOssServerSideEncryption: objectMeta.Encryption}
			sendSuccess(requestId, w, headers)
			return status.Success
		})
}

func HeadObjectHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "HeadObjectHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			ifModifiedSince := req.Header.Get(conf.HTTPHeaderIfModifiedSince)
			ifUnmodifiedSince := req.Header.Get(conf.HTTPHeaderIfUnmodifiedSince)
			ifMatch := req.Header.Get(conf.HTTPHeaderIfMatch)
			ifNoneMatch := req.Header.Get(conf.HTTPHeaderIfNoneMatch)

			headers, rc := api.HeadObject(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req),
				ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch)
			if rc != status.Success {
				return rc
			}
			sendSuccess(requestId, w, headers)
			return status.Success
		})
}

func DeleteObjectHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "DeleteObjectHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			rc := api.DeleteObject(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req))
			if rc != status.Success {
				return rc
			}

			sendNoContent(requestId, w, nil)
			return status.Success
		})
}

func PutObjectAclHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "PutObjectAclHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			permission := req.Header.Get(conf.HTTPHeaderOssObjectACL)
			if len(permission) == 0 {
				permission = req.Header.Get(conf.HTTPHeaderOssACL)
			}

			rc := api.PutObjectAcl(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req), permission)
			if rc != status.Success {
				return rc
			}

			sendSuccess(requestId, w, nil)
			return status.Success
		})
}

func GetObjectAclHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "GetObjectAclHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			policy, rc := api.GetObjectAcl(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req))
			if rc != status.Success {
				return rc
			}

			sendXmlDocument(policy, http.StatusOK, requestId, w, nil)
			return status.Success
		})
}

func PostObjectHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "PostObject",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			err := req.ParseMultipartForm(conf.MaxMemory)
			if err != nil {
				return status.InternalError
			}

			accessKeyId := req.Form.Get(conf.OssFormFieldOSSAccessKeyId)
			signature := req.Form.Get(conf.OssFormFieldSignature)
			policy := req.Form.Get(conf.OssFormFieldPolicy)

			rc := checkPolicySignature(getBucketName(req), policy, accessKeyId, signature)
			if rc != status.Success {
				return rc
			}

			fileUpload, handlerUpload, err := req.FormFile("file")
			if err != nil {
				return status.InternalError
			}
			if fileUpload == nil || handlerUpload == nil {
				return status.FilePartNotExist
			}
			defer fileUpload.Close()

			rc = checkPolicy(req, req.Form, handlerUpload.Size)
			if rc != status.Success {
				return rc
			}

			contentChecksum := req.Header.Get(conf.HTTPHeaderContentMD5)
			bucketName := getBucketName(req)
			objectName := getObjectNamePost(req.Form.Get(conf.OssFormFieldKey), handlerUpload.Filename)

			headers, rc := api.PostObject(requestId, bucketName, objectName, contentChecksum, handlerUpload.Size, req.Form, fileUpload)
			if rc != status.Success {
				return rc
			}

			if req.Form.Get(conf.PolicyConditionsSuccessActionRedirect) != "" {
				sendHeaderOnly(requestId, w, headers)
				http.Redirect(w, req, req.Form.Get(conf.PolicyConditionsSuccessActionRedirect), http.StatusFound)
			} else {
				httpStatus, err := strconv.Atoi(req.Form.Get(conf.PolicyConditionsSuccessActionStatus))
				if err != nil || (httpStatus != http.StatusOK && httpStatus != http.StatusCreated && httpStatus != http.StatusNoContent) {
					sendNoContent(requestId, w, headers)
				} else {
					sendHttpStatus(requestId, w, httpStatus, headers)
				}
			}

			return status.Success
		})

}

func GetObjectMetaHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "GetObjectMetaHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			headers, e := api.GetObjectMeta(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req))
			if e != status.Success {
				return e
			}

			sendSuccess(requestId, w, headers)
			return status.Success
		})
}

func DeleteMultipleObjectsHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "DeleteMultipleObjectsHandler",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			if req.ContentLength == 0 || req.ContentLength > conf.HttpMultiDeleteBodyMaxLength {
				return status.MalformedXML
			}
			contentMD5 := req.Header.Get(conf.HTTPHeaderContentMD5)
			if len(contentMD5) == 0 {
				return status.InvalidDigest
			}

			var wantDelete proto.Delete
			rc := loadAndValidateXmlDocument(&wantDelete, req)
			if rc != status.Success {
				return rc
			}

			deleteResult, rc := api.DeleteMultipleObjects(requestId, getAccessKeyId(req), getBucketName(req), &wantDelete)
			if rc != status.Success {
				return rc
			}

			sendXmlDocument(deleteResult, http.StatusOK, requestId, w, nil)
			return status.Success
		})
}

func PutSymlinkHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "PutSymlink",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			targetObjectName := req.Header.Get(conf.HTTPHeaderOssSymlinkTarget)
			permission := getHeaderString(req, conf.HTTPHeaderOssACL, conf.PermissionObjectDefault)
			rc := api.PutSymlink(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req), targetObjectName, permission, getUserMeta(req), req.ContentLength)
			if rc == status.Success {
				sendSuccess(requestId, w, nil)
			}
			return rc
		})
}

func GetSymlinkHandler(w http.ResponseWriter, req *http.Request) {
	GeneralHandler(w, req, "GetSymlink",
		func(w http.ResponseWriter, req *http.Request, requestId string) status.Status {
			headers, rc := api.GetSymlink(requestId, getAccessKeyId(req), getBucketName(req), getObjectName(req))
			if rc == status.Success {
				sendSuccess(requestId, w, headers)
			}
			return rc
		})
}
